import { parse } from 'node:querystring'
import { FAILED_CODE } from '../core/constant'

type UniCloudEvent = Record<string, any>

interface UniCloudContext {
  APPID?: string
  SOURCE: 'server' | 'http' | 'client' | 'timing' | 'function'
  CLIENTIP: string
  CLIENTUA: string
  OS?: unknown
  PLATFORM?: unknown
  SPACEINFO: {
    provider: 'aliyun' | 'tencent'
    spaceId: string
  }
  DEVICEID?: string
  FUNCTION_NAME: string
  LOCALE?: unknown
  [key: string]: any
}

declare let uniCloud: {
  $args: any
  $ctx: any
  httpclient: any
  database: () => any
}

interface Data { [key: string]: string }

const CONTENT_TYPE = 'content-type'
const JSON_TYPE = 'application/json'
const FORM_TYPE = 'application/x-www-form-urlencoded'

function initContextType(headers: Data) {
  const key = Object.keys(headers).find(
    key => key.toLowerCase() === CONTENT_TYPE,
  )
  if (key) {
    headers[CONTENT_TYPE] = headers[key].toLowerCase()
    if (key !== CONTENT_TYPE) {
      delete headers[key]
    }
  }
  else {
    headers[CONTENT_TYPE] = JSON_TYPE
  }
}

function extend(event: UniCloudEvent, httpRequest: any, prefix: string) {
  const req = { path: '', method: '', query: {}, params: {}, body: {}, headers: {}, event, cloud: null }

  if (httpRequest) {
    const { path, headers, httpMethod, body, queryStringParameters, isBase64Encoded } = httpRequest
    initContextType(headers)

    req.method = httpMethod
    req.path = path.replace(prefix, '')
    req.headers = headers

    if (httpMethod === 'GET') {
      req.query = queryStringParameters
    }
    else {
      if (body) {
        let res = body
        if (isBase64Encoded) {
          // eslint-disable-next-line node/prefer-global/buffer
          res = Buffer.from(body, 'base64').toString('utf8') // 将base64格式的xml内容转为xml字符串
        }
        const contextType = headers[CONTENT_TYPE]
        if (contextType === JSON_TYPE) {
          try {
            req.body = JSON.parse(res)
          }
          catch (e) { }
        }
        else if (contextType === FORM_TYPE) {
          req.body = parse(res)
        }
      }
    }
  }
  else {
    const { method, path, query, headers } = event
    req.method = method
    req.path = path
    req.headers = headers
    if (method === 'GET') {
      req.query = query
    }
    else {
      req.body = event.body
    }
  }
  return req
}

export const runInUniCloud = typeof uniCloud !== 'undefined'

export default async function uniCloudObjWrap(callback: (arg: any) => Promise<any>, event: UniCloudEvent, global: any, prefix = '/app') {
  const httpRequest = global.getHttpInfo()
  const req = extend(event, httpRequest, prefix)
  req.cloud = global
  const ctx = await callback(req)

  if (ctx.response.status !== 200) {
    const contentTypeHeader = { [CONTENT_TYPE]: JSON_TYPE, 'content-disposition': 'inline' }

    const e = ctx.response.body
    const ret = {
      code: e.code || FAILED_CODE,
      message: e.message,
    }
    if (ctx.app.config.debug === true) {
      ; (ret as any).stack = e.stack || ''
    }
    return {
      mpserverlessComposedResponse: true, // aliyun
      statusCode: 400,
      headers: contentTypeHeader,
      body: JSON.stringify(ret),
    }
  }

  if (httpRequest) {
    const contextType = ctx.response.headers[CONTENT_TYPE] || JSON_TYPE
    const contextDisposition = { 'content-disposition': ctx.response.headers['content-disposition'] || 'inline' }

    return {
      mpserverlessComposedResponse: true, // aliyun
      isBase64Encoded: !!ctx.isBase64Encoded,
      statusCode: ctx.response.status,
      headers: Object.assign(ctx.response.headers, { [CONTENT_TYPE]: contextType }, contextDisposition),
      body: contextType === JSON_TYPE ? JSON.stringify(ctx.response.body) : ctx.response.body,
    }
  }

  return ctx.response.body
}
